#include <iostream>
#include "stdio.h"
#include "time.h"

using namespace std;

void grayFilter (int tamanhoArquivo, unsigned char *linha3, char *saida3){
	int enderecoFim=tamanhoArquivo;
	clock_t comeco, fim;
	unsigned char numParcial[4], soma[4] = {255, 255, 255, 255}, zeros[4]={0, 0, 0, 0};
	double tempoGasto;

	comeco = clock();
	__asm{ // VERSAO EM MMX
		// PARTE EM SIMD QUE NAO PRECISA ESTAR NO LOOP
		movd mm1, soma
		movd mm2, zeros
		punpcklbw mm1, mm2 // TRANSFORMA OS 4 CHAR EM WORD

		// PARTE SEQUENCIAL
		mov ebx, 0
		mov ecx, linha3[ebx] // PASSANDO O ENDERECO QUE ESTA APONTANDO
		mov eax, saida3[ebx] // PASSANDO O ENDERECO QUE ESTA APONTANDO
		add enderecoFim, ecx // GUARDANDO EM ENDERECOFIM A ULTIMA POSICAO QUE TEM ALGUMA COR
		mov edx, 0
montaNumParcial: 
		mov bl, [ecx]
		mov numParcial[edx], bl
		inc ecx
		inc edx
		cmp edx, 4 // VENDO SE JA PREENCHEU O VETOR NUMPARCIAL
		jz simd
		cmp ecx, enderecoFim // VENDO SE JA LEU TODAS AS CORES POSSIVEIS PARA COMPLETAR COM 0 O RESTO DO VETOR
		jnz montaNumParcial
naoConta:
		mov numParcial[edx], 0
		inc edx
		cmp edx, 4
		jnz naoConta
simd: // RESTO DA PARTE EM SIMD
		movd mm0, numParcial
		punpcklbw mm0, mm2 // TRANSFORMA OS 4 CHAR EM WORD
		paddw mm0, mm1
		psrlw mm0, 1 // DIVIDE POR 2 TUDO
		packuswb mm0, mm2 // COLOCA TUDO NOS 32 BITS DA ESQUERDA COMO UNSIGNED
		movd [eax], mm0 // MOVE PARA A SAIDA3 PARA SER COLOCADO NA NOVA IMAGEM
		add eax, 4
		mov edx, 0
		cmp ecx, enderecoFim // VENDO SE JA LEU TODAS AS CORES POSSIVEIS PARA SAIR DO FILTRO
		jb montaNumParcial
	}
	fim = clock();
	tempoGasto = ((double) (fim - comeco)) / CLOCKS_PER_SEC;
	cout << "Tempo gasto (em segundos): " << tempoGasto << endl;
}

int main(int argc, char* argv[]){
	FILE *imagemEntrada, *imagemSaida;
	int largura, altura, profundidade, tamanhoArquivo=1, tamanhoSaida; // COMECA COM 1 PELO \0
	char linha1[3], *saida3;
	unsigned char *linha3; // TEM QUE SER UNSIGNED PARA NAO MANDAR VALORES NEGATIVOS PARA A HORA DE CONVERTER

	imagemEntrada = fopen(argv[1], "rb");
	imagemSaida = fopen(argv[2], "wb");

	if ((imagemEntrada != NULL) && (imagemSaida != NULL)){

		fread(linha1, 1, 3, imagemEntrada);
		fscanf(imagemEntrada, "%d %d %d\n", &largura, &altura, &profundidade);

		tamanhoArquivo += largura * altura * 3; // 3 CANAIS, CADA UM COM 1 CHAR
		linha3 = (unsigned char*) malloc (tamanhoArquivo*sizeof(unsigned char));
		tamanhoArquivo--; //POIS O SAIDA3 NAO PRECISA DO \0
		if (tamanhoArquivo % 4 != 0) // SE JA FOR DIVISIVEL POR 4 NAO PRECISA ACRESCENTAR + ESPACO		
			tamanhoSaida = tamanhoArquivo + (4 - (tamanhoArquivo % 4));		
		else
			tamanhoSaida = tamanhoArquivo;
		saida3 = (char*) malloc (tamanhoSaida*sizeof(char));

		if ((linha3 != NULL) && (saida3 != NULL)){

			tamanhoArquivo++;
			fread(linha3, 1, tamanhoArquivo, imagemEntrada);
			tamanhoArquivo--;

			grayFilter (tamanhoArquivo, linha3, saida3);

			fprintf (imagemSaida,"%c%c%c",linha1[0] ,linha1[1],10);
			fprintf (imagemSaida,"%d %d %d%c", largura, altura, profundidade, 10);
			fwrite (saida3, 1, tamanhoArquivo, imagemSaida);
			fprintf (imagemSaida,"%c",10);

			free (linha3);
			free (saida3);
		}
		else
			cout << "Nao foi possivel alocar memoria" << endl;

		fclose(imagemEntrada);
		fclose(imagemSaida);
	}
	else
		cout << "Houve um problema com alguma das imagens" << endl;
	return 0;
}